package errors

import (
	"encoding/json"
	"errors"
	"regexp"
	"runtime"
	"strings"
)

var (
	reInit    = regexp.MustCompile(`init·?\d+$`) // main.init·1
	reClosure = regexp.MustCompile(`func·?\d+$`)
)

// Error 为接口类型, 是 error 接口类型的扩展,用于给错误增加调用栈信息, 同事支持错误的多级嵌套包装,支持错误码格式, 为了方便使用,可以定义一下的辅助函数
type Error interface {
	Caller() []CallerInfo
	Wraped() []error
	Code() int
	error

	private()
}

type _Error struct {
	XCode int `json:"code"`
	XError error `json:"Error"`
	XCaller []CallerInfo `json:"Caller,omitempty"`
	XWraped []error `json:"Wraped,omitempty"`
}

type CallerInfo struct {
	FuncName string
	FileName string
	FileLine int
}

// 调用详情
func callerInfo(skip int)(name, file string, line int, ok bool) {
	pc, file,line, ok := runtime.Caller(skip)
	if !ok {
		name = "???"
		file = "???"
		line = 1
		return
	}

	name = runtime.FuncForPC(pc).Name()
	if reInit.MatchString(name) {
		name = reInit.ReplaceAllString(name, "init")
	}
	if reClosure.MatchString(name) {
		name  = reClosure.ReplaceAllString(name, "func")
	}
	if idx := strings.LastIndex(file, "/");idx >= 0 {
		file = file[idx+1:]
	} else if idx = strings.LastIndex(file, "\\"); idx >= 0 {
		file = file[idx+1:]
	}
	return
}

func Caller(skip int) []CallerInfo {
	var infos []CallerInfo
	for ; ; skip++ {
		name, file, line, ok := callerInfo(skip+1)
		if !ok {
			return infos
		}
		if strings.HasPrefix(name, "runtime.") {
			return infos
		}
		
		infos = append(infos, CallerInfo{
			FuncName: name,
			FileName: file,
			FileLine: line,
		})
	}
	panic("unreached!")
}

func New(msg string) error {
	return  &_Error{
		XCaller: Caller(2),
		XError: errors.New(msg),
	}
}

func (p *_Error) Caller() []CallerInfo {
	return p.XCaller
}

func (p *_Error) Wraped() []error  {
	return p.XWraped
}

func (p *_Error) Error() string  {
	return string(p)
}